#include<iostream>
#include<string>
#include<cstdlib>  
#include<ctime>   
#include<cmath>
#include<vector>
#include<iomanip>
#include"readfile.cpp"
using namespace std;

double R_V = 0.001;       //全局变量：A事件的电压值V的精度
double R_T = 0.001;       //全局变量：D事件的dT的精度

double getRandom_V()    //生成指定精度R_V的[0,1]之间的随机数V作为事件A的电压
{
    double randValue = static_cast<double>(rand()) / RAND_MAX; //生成[0,1]之间的随机数
    return round(randValue / R_V) * R_V; //调整到指定精度
}

double getRandom_dT()  //生成指定精度R_T的[0,1]之间的随机数，作为事件D的dT
{
    double randomValue;
    do 
    {
        //生成 (0.1, 0.3) 之间的随机数
        randomValue = 0.1 + static_cast<double>(rand()) / RAND_MAX * (0.3 - 0.1);
        //按照 R 进行精度控制
        randomValue = round(randomValue / R_T) * R_T;
    } while (fabs(randomValue - 0.1) < EPSILON || fabs(randomValue - 0.3) < EPSILON);//确保不取边界值0.1和0.3

    return randomValue;
}


class Event 
{
public:
    double dT;
    double T;                    
    int bM;

    virtual void print_event(std::ostream& out = std::cout) = 0; //打印
    virtual ~Event(){}
};

class Aevent : public Event
{
public:
    double V;
    double Vth;

    Aevent(double v, double vth, double dt, double t, int bm)
    {
        V = v;
        Vth = vth;
        dT = dt;
        T = t;
        bM = bm;
    }
    void print_event(std::ostream& out = std::cout) override{
        out << "A(" << V << " " << Vth << " " << dT << " " << std::setw(2) << std::setfill(' ') << bM << ")" << "\t";
    }

};

class Devent : public Event
{
public:
    int V;

    Devent(int v, double dt, double t, int bm)
    {
        V = v;
        dT = dt;
        T = t;
        bM = bm;
    }
    void print_event(std::ostream& out = std::cout) override{
        out << "D(" << V << " " << dT << " " <<std::setw(2) << std::setfill(' ') << bM << ")" << "\t";
    }

};


//仿真器,分为A/D两种
class Simulator
{
public:
    virtual void Push_Sim(Event*& p) = 0;      //推进仿真,产生的对应事件存储于指针p中
    virtual void Syn(Event* p1, Event*& p2) = 0;    //被同步，指针p1为事件的产生者，p2存储产生的事件
};

class Simulator_A : public Simulator
{
private:
    double Vth;
    double dT;
public:
    vector<Aevent*> list_A;           //存储A事件
    Simulator_A(double vth, double dt)
    {
        Vth = vth;
        dT = dt;
    }
    void Push_Sim(Event*& p) override{    //推进仿真
        double newT;
        if(list_A.size() == 0)
            newT = 0;
        else
            newT = list_A[list_A.size()-1]->T + dT;
        double newV = getRandom_V();
        Aevent* tmp = new Aevent(newV, Vth, dT, newT, 0);   //产生新的A事件
        p = tmp;      //返回事件
        list_A.push_back(tmp);    //存储事件
    }
    void Syn(Event* p1, Event*& p2) override{    //被同步
        Devent* p_D = static_cast<Devent*>(p1);   //指针显式转换
        double newT = p_D->T;
        double newV = p_D->V;
        Aevent* tmp = new Aevent(newV, Vth, dT, newT, -1);   //产生被同步的A事件
        p2 = tmp;
        list_A.push_back(tmp);
    }
};

class Simulator_D : public Simulator
{
public:
    vector<Devent*> list_D;         //存储D事件
    void Push_Sim(Event*& p) override{   //推进仿真
        double newT = list_D[list_D.size()-1]->T + list_D[list_D.size()-1]->dT;    //由于第一个D事件一定是由被同步函数Syn()产生，产生第一个事件后才可能推进仿真，所以这里不需要对list_D.size()判0
        int newV = list_D[list_D.size()-1]->V == 0 ? 1 : 0;     //对前一个D事件的V进行翻转
        double new_dT = getRandom_dT();      //在(0.1, 0.3)范围内随机取dT
        Devent* tmp = new Devent(newV, new_dT, newT, 1);   //产生新的D事件
        p = tmp;
        list_D.push_back(tmp);
    }
    void Syn(Event* p1, Event*& p2) override{   //被同步
        Aevent* p_A = static_cast<Aevent*>(p1);   //指针显式转换
        double newT = p_A->T;
        double new_dT = getRandom_dT();      //在(0.1, 0.3)范围内随机取dT
        int newV = (list_D.size() > 0 ? list_D[list_D.size()-1]->V : 0) * static_cast<int>(p_A->V >= p_A->Vth);
        Devent* tmp = new Devent(newV, new_dT, newT, -1);     //产生新的D事件
        p2 = tmp;
        list_D.push_back(tmp);
    }
};

//仿真底板
class Sim_plate
{
private:
    double dT;
    double Vth;
    double Tsim;
    Simulator_A* Sim_A;
    Simulator_D* Sim_D;
    void print_info(bool print_first)  //信息打印函数，由函数start_Sim调用，目的是当A事件或D事件达到一定数目时，能够打印出仿真信息并清除无用的A事件和D事件（实际上只留下最新一个A事件和D事件，以便进行后续仿真），以实现一定程度上的“边仿真边打印”
    {       //根据A事件和D事件的特点，我们可以得到一个结论：在任意的一个仿真时间节点，一定有一个A事件，可能有一个D事件，也就是说A可以单独出现，D不能。所以我们有参数print_first  
            //bool print_first参数表示是否打印第一个A事件和第一个D事件，即当start_Sim第一次调用print_info函数时，传入参数print_first = true, 之后调用都令这个参数为false
            //这个操作的目的是防止每次留下的那个A和D事件重复打印
        double T;
        int decimal_places = -log10(R_T);  //计算输出保留的小数位数
        int count_A = print_first == true ? 0 : 1;
        int count_D = print_first == true ? 0 : 1;
        int end_A = Sim_A->list_A.size() - 1; 
        int end_D = Sim_D->list_D.size() - 1; 

        while(count_A <= end_A)       //打印
        {
            T = Sim_A->list_A[count_A]->T;
            cout << fixed << setprecision(decimal_places) << T << "\t";
            Sim_A->list_A[count_A]->print_event();
            count_A++;          
            if((count_D <= end_D) && (compare_double(T,Sim_D->list_D[count_D]->T) == 0))
            {
                Sim_D->list_D[count_D]->print_event();
                count_D++;
            }
            cout << "\n";
        }

        if(Sim_A->list_A.size() != 0)
        {
            for(int i = 0; i < (static_cast<int>(Sim_A->list_A.size()) - 1); i++)   //除了最新的一个A事件都清除
                delete Sim_A->list_A[i];
            Aevent* tmp_A = Sim_A->list_A[Sim_A->list_A.size() - 1];
            Sim_A->list_A.clear();
            Sim_A->list_A.push_back(tmp_A);
        }

        if(Sim_D->list_D.size() != 0)
        {
            for(int i = 0; i < (static_cast<int>(Sim_D->list_D.size()) - 1); i++)   //除了最新的一个D事件都清除
                delete Sim_D->list_D[i];
            Devent* tmp_D = Sim_D->list_D[Sim_D->list_D.size() - 1];
            Sim_D->list_D.clear();
            Sim_D->list_D.push_back(tmp_D);
        }
    }
    void print_end()   //打印结束信息
    {   
        if(compare_double(Sim_A->list_A.back()->T, Tsim) == 0)   //如果最后一个A事件发生的时间恰好和Tsim相等
            cout << "FINISH";      //另起一行打印FINISH（“另起一行”在print_info中已经完成）
        else   
        {
            int decimal_places = -log10(R_T);  //计算输出保留的小数位数
            cout << fixed << setprecision(decimal_places) << Tsim << "\tFINISH";   //否则另起一行打印Tsim和“FINISH”
        }
            
    }
public:
    Sim_plate(double dt, double vth, double tsim)
    {
        dT = dt;
        Vth = vth;
        Tsim = tsim;
        Sim_A = new Simulator_A(vth,dt);   //初始化A/D仿真器
        Sim_D = new Simulator_D();
    }
    void start_Sim()     //开始仿真
    {
        double T = 0.0;
        const int print_limit = 10000;   //仿真达到这个次数，调用一次print_info函数
        int count = 0;   //count是仿真次数的计数
        Aevent* p_A = NULL;   
        Devent* p_D = NULL;   //这两个指针分别指向最新的A事件和D事件
        Event *tmp_A, *tmp_D;
        
        //先进行一次A仿真启动
        Sim_A->Push_Sim(tmp_A);
        p_A = static_cast<Aevent*>(tmp_A);
        T = p_A->T;
        count++;
        //再进行接下来的仿真
        double former_V; //记录前一个A事件的电压值，以便判断是否跨过阈值电压
        while(((p_D == NULL) && (compare_double(T+dT, Tsim) <= 0)) || ((p_D != NULL) && ((compare_double(T+dT, Tsim) <= 0) || (compare_double(p_D->T + p_D->dT, Tsim) <= 0))))      
        {  //如果“p_D == NULL 的情况下：下一个A事件发生时间不晚于Tsim; 或  p_D != NULL的情况下：下一个A事件发生时间不晚于Tsim 或 下一个D事件发生时间不晚于Tsim” ，则仿真继续
            former_V = p_A->V;
            if(p_D == NULL)   //如果还没有D事件发生
            {
                Sim_A->Push_Sim(tmp_A);   //推进A仿真
                p_A = static_cast<Aevent*>(tmp_A);
                if(((compare_double(former_V, Vth) <= 0) && (compare_double(p_A->V, Vth) > 0)) || ((compare_double(former_V, Vth) > 0) && (compare_double(p_A->V, Vth) <= 0)))   //如果跨过了阈值电压，则要产生事件D
                {
                    p_A->bM = 1;
                    Sim_D->Syn(p_A, tmp_D);            //D进行同步
                    p_D = static_cast<Devent*>(tmp_D);
                }   
                T = p_A->T;
                count++;   
            }
            else
            {    
                
                if(compare_double(T + dT, p_D->T + p_D->dT) < 0)    //如果下一个A事件在下一个D事件之前发生
                {
                    Sim_A->Push_Sim(tmp_A);   //推进A仿真
                    p_A = static_cast<Aevent*>(tmp_A);
                    if(((compare_double(former_V, Vth) <= 0) && (compare_double(p_A->V, Vth) > 0)) || ((compare_double(former_V, Vth) > 0) && (compare_double(p_A->V, Vth) <= 0)))   //如果跨过了阈值电压，则要产生事件D
                    {
                        p_A->bM = 1;
                        Sim_D->Syn(p_A, tmp_D);        //D进行同步
                        p_D = static_cast<Devent*>(tmp_D);
                    }   
                    T = p_A->T;
                    count++;  
                }
                else if(compare_double(T + dT, p_D->T + p_D->dT) > 0)  //如果下一个A事件发生时间晚于下一个D事件，那么推进D仿真
                {
                    Sim_D->Push_Sim(tmp_D);   //推进D仿真
                    p_D = static_cast<Devent*>(tmp_D);   
                    Sim_A->Syn(p_D, tmp_A);     //A进行同步
                    p_A = static_cast<Aevent*>(tmp_A);
                    T = p_A->T;
                    count++;
                }
                else
                {
                    Sim_A->Push_Sim(tmp_A);   //先推进A仿真
                    p_A = static_cast<Aevent*>(tmp_A);
                    if(((compare_double(former_V, Vth) <= 0) && (compare_double(p_A->V, Vth) > 0)) || ((compare_double(former_V, Vth) > 0) && (compare_double(p_A->V, Vth) <= 0)))  //如果跨过了阈值电压，则优先产生事件D
                    {
                        p_A->bM = 1;
                        Sim_D->Syn(p_A, tmp_D);        //D进行同步
                        p_D = static_cast<Devent*>(tmp_D);
                    }   
                    else       //如果没有跨过阈值电压，则D推进事件，D事件产生A事件覆盖当前A事件
                    {
                        delete  Sim_A->list_A.back();
                        Sim_A->list_A.pop_back();      //先删除当前A事件
                        Sim_D->Push_Sim(tmp_D);   //再推进D仿真
                        p_D = static_cast<Devent*>(tmp_D);   
                        Sim_A->Syn(p_D, tmp_A);     //A进行同步
                        p_A = static_cast<Aevent*>(tmp_A);
                    }
                    T = p_A->T;
                    count++;
                }
                
            }
            if(count % print_limit == 0)   //每当进行print_limit次仿真，输出一次结果
            {
                if(count == print_limit)
                    print_info(true);
                else
                    print_info(false);
            }
        }
        //仿真结束后，打印剩下的信息
        if(count <= print_limit)
            print_info(true);
        else
            print_info(false);
        print_end();   //打印结束信息
    }
};

int main()
{
    
    srand(time(0));
    string filename = "con.txt";
	string outfile_name = "sim_res.txt";

	double dt, vth, tsim;
    //读取参数，检验是否有报错
    try {
        readFile(dt,vth,tsim,filename);
    } catch (const std::exception& e) {
        std::cerr << "错误信息: " << e.what() << std::endl;
        return 1;
    }

    ofstream outfile(outfile_name);  //打开输出文件
    if (!outfile.is_open()) {
        std::cerr << "无法打开文件！" << std::endl;
        return 1;
    }

    // 使用streambuf来重定向cout到文件
    std::streambuf *backup = std::cout.rdbuf(); // 备份原始的cout缓冲区
    std::cout.rdbuf(outfile.rdbuf());        // 将cout的缓冲区设置为文件的缓冲区

    Sim_plate* Sp1;
    Sp1 = new Sim_plate(dt, vth, tsim);   
    Sp1->start_Sim();

    // 重置cout的缓冲区到原始的缓冲区，这样cout的输出就会回到标准输出
    std::cout.rdbuf(backup);
    outfile.close();
    return 0;
}
